/*
 * ml20dll
 * This source code is under the GNU Public License (GPL).
 * Written by Ramiro Polla
 * Initial release: july 14th, 2005
 *
 * this dll reads ml20 files
 */

#include "ml20dll.h"

static int vc_is_frame_complete( vcs *tmp );
static void vc_fit_before( vcs *new_u, vcs *tmp_u );
static void vc_fit_after( vcs *new_u, vcs *tmp_u );
static void vc_switch_free( vcs *new_u, vcs *tmp_u );

vcs *vc_next_keyframe( vcs *frame, FILE *in, ML20F *ml20f )
{
    vcs *tmp;
    for( tmp = (vcs *) vc_next_frame( frame, in, ml20f ) ; tmp != (vcs *) -1 ; tmp = (vcs *) vc_next_frame( tmp->next, in, ml20f ) )
        if( IS_SET( ml20f->h.ff, 1 ) )
            break;

    if( tmp == (vcs *) -1 )
        return (vcs *) -1;

    return tmp;
}

vcs *vc_next_frame( vcs *tmp, FILE *in, ML20F *ml20f )
{
    vcs *tmp_v = tmp;

    while( 1 ) {
        if( !tmp_v )
            break;

        if( tmp_v->vc_hdr.frame_part == 0 )
            if( vc_is_frame_complete( tmp_v ) )
                break;

        tmp_v = tmp_v->next;
    }

    if( tmp_v ) {
        //TODO
        //fseek( in, tmp_v->filepos, SEEK_SET );
        //fread( &ml20f->h, 1, sizeof(ML20H), in );
        ml20f->h.ff = !tmp_v->vc_hdr.nkeyframe;
        ml20f->h.width = 320;
        ml20f->h.height = 240;
        ml20f->h.time = tmp_v->vc_hdr.timestamp;
        return tmp_v;
    }

    return (vcs *) -1;
}

static int vc_is_frame_complete( vcs *tmp )
{
    int i;
    int i2 = tmp->vc_hdr.frame_tot;
    int i3 = tmp->vc_hdr.frame_num;

    //return 1 if the frame is complete
    for( i = 0 ; tmp != 0 ; tmp = tmp->next, i++ ) {
        if( (tmp->vc_hdr.frame_part != i) || (tmp->vc_hdr.frame_num != i3) )
            return 0;
        if( i == i2-1 )
            return 1;
    }

    return 0;
}

static void vc_fit_before( vcs *new_v, vcs *tmp_v )
{
    new_v->previous = tmp_v->previous;
    if( new_v->previous )
        new_v->previous->next = new_v;

    new_v->next = tmp_v;
    tmp_v->previous = new_v;
}

static void vc_fit_after( vcs *new_v, vcs *tmp_v )
{
    new_v->next = tmp_v->next;
    if( tmp_v->next )
        tmp_v->next->previous = new_v;

    tmp_v->next = new_v;
    new_v->previous = tmp_v;
}

static void vc_switch_free( vcs *new_v, vcs *tmp_v )
{
    new_v->previous = tmp_v->previous;
    new_v->next = tmp_v->next;

    if( new_v->previous )
        new_v->previous->next = new_v;
    if( new_v->next )
        new_v->next->previous = new_v;
    free(tmp_v);
}

vcs *read_vcs( FILE *in, int pos )
{
    vcs tnew_vcs;
    vcs *new_vcs = &tnew_vcs;

    memset( new_vcs, 0x00, sizeof(vcs) );

    fseek( in, pos, SEEK_SET );
    do {
        /*
        if( !fread( &new_vcs->time, sizeof( uint32_t ), 1, in ) )
            return 0;
        */
        if( !fread( &new_vcs->vc_hdr, sizeof( struct vcinthdr ), 1, in ) )
            return 0;
        new_vcs->filepos = ftell( in );
        if( fseek( in, new_vcs->vc_hdr.size, SEEK_CUR ) )
            return 0;
    }
    while( new_vcs->vc_hdr.code != 0x62 ); //only video packets

    new_vcs = malloc( sizeof(vcs) );
    memcpy( new_vcs, &tnew_vcs, sizeof(vcs) );
    return new_vcs;
}

void clean_vcs( vcs *first_v )
{
    vcs *tmp_v;
    vcs *p_v;

    tmp_v = first_v;
    p_v = first_v->next;
    while( tmp_v ) {
        free( tmp_v );
        tmp_v = p_v;
        if( tmp_v )
            p_v = tmp_v->next;
    }
}

//return
// -1 if first_v
// 0 if in between
// 1 if last_v
void fit_vcs_part( vcs **first_v, vcs **last_v, vcs *new_v )
{
    vcs *tmp_v;
    int ret = 0;

    for( tmp_v = *last_v ; tmp_v != 0 ; tmp_v = tmp_v->previous ) {
        int8_t dif_num = new_v->vc_hdr.frame_num - tmp_v->vc_hdr.frame_num;

        if( !dif_num ) {
            if( new_v->vc_hdr.frame_part == tmp_v->vc_hdr.frame_part ) {
                if( new_v->vc_hdr.retransmission > tmp_v->vc_hdr.retransmission ) {
                    ret = 1;
                    vc_switch_free( new_v, tmp_v );
                } else {
                    // I already have a better version of this packet,
                    // it took too long to get to me.
                    // Get rid of it.
                    free( new_v );
                    new_v = NULL;
                }
                break;
            } else
            if( new_v->vc_hdr.frame_part > tmp_v->vc_hdr.frame_part ) {
                ret = 1;
                vc_fit_after( new_v, tmp_v );
                break;
            }
        } else
        if( dif_num > 0 ) {
            ret = 1;
            vc_fit_after( new_v, tmp_v );
            break;
        }
    }

    if( new_v ) {
        if( ret == 0 )
            vc_fit_before( new_v, *first_v );

        if( !new_v->next )
            *last_v = new_v;
        if( !new_v->previous )
            *first_v = new_v;
    }
}
